package pl.enigmatic.time;

public abstract class TimerTask extends java.util.TimerTask {

	private Timer timer = null;
	/** counts elapsing time */
	private final TimeCounterDouble counter = new TimeCounterDouble();
	/** <code>true</code>, if <code>this</code> task is not bounded by any specified duration time */
	private final boolean infinite;
	/** amount of time (in seconds) for how long <code>this</code> should run */
	private final double duration;
	private double lastAlpha = 0;

	public TimerTask(final double duration) {
		infinite = false;
		this.duration = duration;
	}

	public TimerTask() {
		infinite = true;
		duration = Double.MAX_VALUE;
	}

	@Override
	public final void run() {
		final double dt = counter.getDelta();
		if (infinite || duration > counter.getMeasuredTime()) {
			run(dt);
		} else {
			finished();
			cancel();
			timer.purge();
		}
	}

	/**
	 * Called as the main function of this thread execution.
	 * @param dt difference between the time of last execution and now
	 */
	public abstract void run(double dt);

	/**
	 * Method invoked only once, when <code>this</code> task has been created with a specified <code>duration</code> time, and after it is done (and
	 * the task will be cancelled after this call). Free to override.
	 */
	public void finished() {}

	/**
	 * @return the infinite
	 */
	public boolean isInfinite() {
		return infinite;
	}

	/**
	 * @return the duration
	 */
	public double getDuration() {
		return duration;
	}

	boolean setTimerAndSchedule(final Timer timer) {
		if (this.timer == null && timer != null) {
			this.timer = timer;
			return true;
		}
		return false;
	}

	public boolean isRunning() {
		return timer != null && (infinite || duration > counter.getMeasuredTime());
	}

	/**
	 * Returns alpha value. This call makes sense only when the duration is not 0, and the task is not infinite.
	 * @return the factor of time running and duration
	 * @throws ArithmeticException if duration was 0
	 */
	public double getAlpha() {
		return counter.getMeasuredTime() / duration;
	}

	/**
	 * Returns delta alpha value. This call makes sense only when the duration is not 0, and the task is not infinite.
	 * @throws ArithmeticException if duration was 0
	 */
	public double getDeltaAlpha() {
		final double last = lastAlpha;
		lastAlpha = getAlpha();
		return lastAlpha - last;
	}

	/**
	 * @return
	 * @see pl.enigmatic.time.TimeCounterDouble#getMeasuredTime()
	 */
	public double getMeasuredTime() {
		return counter.getMeasuredTime();
	}

	/**
	 * @return
	 * @see pl.enigmatic.time.TimeCounterDouble#getElapsedTime()
	 */
	public double getElapsedTime() {
		return counter.getElapsedTime();
	}
}
